﻿/*
 * Copyright (c) 2009, Thiago M. Sayao <thiago.sayao@gmail.com>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 *  * Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the author nor the names of its contributors may be 
 *    used to endorse or promote products derived from this software without 
 *    specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 * POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Specialized;
using System.Globalization;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
using System.Text;
using DotNetBurner.Web.Resources;

namespace DotNetBurner.Web
{
    public static class HtmlHelperExtensionsPager
    {
        const int SliderSize = 8;

        public static string Pager(this HtmlHelper helper, IPagedViewData viewData)
        {
            StringBuilder str = new StringBuilder();

            //there is no paging, return 
            if (viewData.ItemCount <= viewData.ItemsPerPage)
                return String.Empty;

            str.Append("<div class=\"pager\">");

            //previous
            if (viewData.CurrentPage > 1)
                str.Append(GetPagerLink(helper, Strings.Previous, (viewData.CurrentPage - 1)));
            else
                str.AppendFormat("<span class=\"disabled\">{0}</span>", Strings.Previous);

            int start, end;

            CalculateStartAndEnd(viewData, out start, out end);

            if (start > 3)
            {
                str.Append(GetPagerLink(helper, "1", 1));
                str.Append(GetPagerLink(helper, "2", 2));
                str.Append("...");
            }

            for (int i = start; i <= end; i++)
            {
                if (i == viewData.CurrentPage)
                    str.AppendFormat("<span class=\"current\">{0}</span>", i);
                else
                    str.Append(GetPagerLink(helper, i.ToString(CultureInfo.InvariantCulture), i));
            }

            if (end < (viewData.PageCount - 3))
            {
                str.Append("...");
                str.Append(GetPagerLink(helper, (viewData.PageCount - 1).ToString(CultureInfo.InvariantCulture), (viewData.PageCount - 1)));
                str.Append(GetPagerLink(helper, viewData.PageCount.ToString(CultureInfo.InvariantCulture), viewData.PageCount));
            }

            if (viewData.CurrentPage < viewData.PageCount)
                str.Append(GetPagerLink(helper, Strings.Next, (viewData.CurrentPage + 1)));
            else
                str.AppendFormat("<span class=\"disabled\">{0}</span>", Strings.Next);

            str.Append("</div>");

            return str.ToString();
        }

        private static string GetPagerLink(HtmlHelper helper, string text, int page)
        {
            var values = new RouteValueDictionary();

            foreach (var item in helper.ViewContext.RouteData.Values)
            {
                if ((string.Compare(item.Key, "controller", StringComparison.OrdinalIgnoreCase) != 0) &&
                    (string.Compare(item.Key, "action", StringComparison.OrdinalIgnoreCase) != 0))
                {
                    values.Add(item.Key, item.Value);
                }
            }

            if (page > 0)
                values["page"] = page;

            NameValueCollection queryString = helper.ViewContext.HttpContext.Request.QueryString;

            foreach (string key in queryString)
                if (key != null && !string.IsNullOrEmpty(queryString[key]) && !values.ContainsKey(key))
                    values[key] = queryString[key];

            string routeName = (string)helper.ViewContext.RouteData.DataTokens["RouteName"];

            if(!string.IsNullOrEmpty(routeName))
            {
                //this is faster
                return helper.RouteLink(text, routeName, values).ToString();
            }
            else
            {
                //this is slower
                string actionName = helper.ViewContext.RouteData.Values["action"].ToString();
                return helper.ActionLink(text, actionName, values).ToString();
            }
        }

        static void CalculateStartAndEnd(IPagedViewData viewData, out int start, out int end)
        {
            int pageCount = viewData.PageCount;

            if (pageCount <= SliderSize)
            {
                start = 1;
                end = pageCount;
            }
            else
            {
                int middle = (int)Math.Ceiling(SliderSize / 2d) - 1;
                int below = (viewData.CurrentPage - middle);
                int above = (viewData.CurrentPage + middle);

                if (below < 4)
                {
                    above = SliderSize;
                    below = 1;
                }
                else if (above > (pageCount - 4))
                {
                    above = viewData.PageCount;
                    below = (pageCount - SliderSize);
                }

                start = below;
                end = above;
            }
        }
    }
}
